﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using Microsoft.SharePoint;
using WeMakeSP.ImageResizer.Configuration;
using WeMakeSP.ImageResizer.Events;
using WeMakeSP.ImageResizer.Services.ServiceContract;
using WeMakeSP.Platform.SharePoint.Configuration;
using WeMakeSP.Platform.SharePoint.Administration;
using Microsoft.SharePoint.Administration;

namespace WeMakeSP.ImageResizer.Services.ServiceImplementation
{
    /// <summary>
    /// The implementation for the image resizer service.
    /// </summary>
    public class ImageResizerService : IImageResizerService
    {
        #region Fields

        /// <summary>
        /// The web field.
        /// </summary>
        private SPWeb webField = null;

        /// <summary>
        /// The web property bag field.
        /// </summary>
        private SPWebPropertyBag webPropertyBagField = null;

        #endregion

        #region Constructors

        /// <summary>
        /// Prevents a default instance of the <see cref="ImageResizerService"/> class from being created.
        /// </summary>
        private ImageResizerService()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ImageResizerService"/> class.
        /// </summary>
        /// <param name="web">The SharePoint web.</param>
        public ImageResizerService(SPWeb web)
            : this()
        {
            // Guard.

            if (web == null)
            {
                throw new ArgumentNullException("web");
            }

            this.webField = web;
        }

        #endregion

        #region Protected Properties

        /// <summary>
        /// Gets the web property bag.
        /// </summary>
        /// <value>The web property bag.</value>
        protected SPWebPropertyBag WebPropertyBag
        {
            get
            {
                if (this.webPropertyBagField == null)
                {
                    this.webPropertyBagField =
                        new SPWebPropertyBag(this.webField);
                }

                // Returns.

                return
                    this.webPropertyBagField;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Gets the picture library configuration.
        /// </summary>
        /// <param name="listId">The list id.</param>
        /// <returns>The image resizer configuration for a picture library.</returns>
        public ImageResizerPictureLibraryConfiguration GetPictureLibraryConfiguration(Guid listId)
        {
            try
            {
                string configurationIdentifier =
                    this.GeneratePictureLibraryToken(listId);

                // Get the xml representation saved in a property bag.

                string configurationToken =
                    this.WebPropertyBag[configurationIdentifier];

                ImageResizerPictureLibraryConfiguration configuration = null;

                // The configuration already exists ?

                if (configurationToken != null)
                {
                    // Unserialize the data.

                    System.Xml.Serialization.XmlSerializer serializer =
                                new System.Xml.Serialization.XmlSerializer(typeof(PictureLibraryPropertyBag));

                    using (StringReader configurationTokenReader = new StringReader(configurationToken))
                    {
                        using (System.Xml.XmlReader reader = System.Xml.XmlTextReader.Create(configurationTokenReader))
                        {
                            // Cast the deserialized object to a PictureLibraryPropertyBag.

                            PictureLibraryPropertyBag propertyBagConfiguration =
                                (PictureLibraryPropertyBag)serializer.Deserialize(reader);

                            // Adapt the entity.

                            configuration =
                                new ImageResizerPictureLibraryConfiguration()
                                {
                                    Active = propertyBagConfiguration.Active,
                                    Action = (ImageResizerAction)Enum.Parse(typeof(ImageResizerAction), propertyBagConfiguration.Action),
                                    HourSchedule = propertyBagConfiguration.HourSchedule,
                                    HoursDelay = propertyBagConfiguration.HoursDelay,
                                    MaxHeight = propertyBagConfiguration.MaxHeight,
                                    MaxWidth = propertyBagConfiguration.MaxWidth,
                                    MinuteSchedule = propertyBagConfiguration.MinuteSchedule,
                                    MinutesDelay = propertyBagConfiguration.MinutesDelay,
                                    Mode = (ImageResizerMode)Enum.Parse(typeof(ImageResizerMode), propertyBagConfiguration.Mode),
                                    Schedule = (ImageResizerSchedule)Enum.Parse(typeof(ImageResizerSchedule), propertyBagConfiguration.Schedule),
                                    SecondsDelay = propertyBagConfiguration.SecondsDelay
                                };
                        }
                    }
                }
                else
                {
                    // First time, no configuration is defined yet.

                    configuration =
                        new ImageResizerPictureLibraryConfiguration();
                }

                // Returns.

                return configuration;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Sets the picture library configuration.
        /// </summary>
        /// <param name="listId">The list id.</param>
        /// <param name="configuration">The configuration.</param>
        public void SetPictureLibraryConfiguration(Guid listId, ImageResizerPictureLibraryConfiguration configuration)
        {
            // Guard.

            if (configuration == null)
            {
                throw new ArgumentNullException("configuration");
            }

            try
            {
                ImageResizerPictureLibraryConfiguration oldConfiguration = null;

                try
                {
                    oldConfiguration =
                        this.GetPictureLibraryConfiguration(listId);
                }
                catch
                {
                    // TODO create a specific exception for configuration loading problems.

                    oldConfiguration =
                        new ImageResizerPictureLibraryConfiguration();
                }

                string configurationIdentifier =
                    this.GeneratePictureLibraryToken(listId);

                // Fill the property bag picture library configuration.

                PictureLibraryPropertyBag newConfiguration =
                    new PictureLibraryPropertyBag()
                    {
                        Action = configuration.Action.ToString(),
                        Active = configuration.Active,
                        HourSchedule = configuration.HourSchedule,
                        HoursDelay = configuration.HoursDelay,
                        MaxHeight = configuration.MaxHeight,
                        MaxWidth = configuration.MaxWidth,
                        MinuteSchedule = configuration.MinuteSchedule,
                        MinutesDelay = configuration.MinutesDelay,
                        Mode = configuration.Mode.ToString(),
                        Schedule = configuration.Schedule.ToString(),
                        SecondsDelay = configuration.SecondsDelay
                    };

                System.Xml.Serialization.XmlSerializer serializer =
                         new System.Xml.Serialization.XmlSerializer(newConfiguration.GetType());

                StringBuilder xml =
                    new StringBuilder();

                System.Xml.XmlWriter writer =
                    System.Xml.XmlWriter.Create(xml);

                serializer.Serialize(writer, newConfiguration);

                // Set the new configuration.

                this.WebPropertyBag[configurationIdentifier] = xml.ToString();

                // Commit the result.

                this.WebPropertyBag.Update();

                SPList list =
                    this.webField.Lists[listId];

                SPWebApplication webApp =
                        this.webField.Site.WebApplication;

                // Remove the handlers.

                ImageResizerHandlers.RemoveHandlersFromList(list);

                // Remove the job.

                if (oldConfiguration.Schedule != ImageResizerSchedule.Immediate)
                {
                    SPJobDefinitionHelper.DeleteJobByName(
                        webApp,
                        PictureLibraryResizerJob.GetJobToken(listId));
                }

                if (newConfiguration.Active == true)
                {
                    // Add the handlers.

                    ImageResizerHandlers.AddHandlersToList(list);

                    // Remove the job.

                    if (oldConfiguration.Schedule != ImageResizerSchedule.Immediate)
                    {
                        SPJobDefinitionHelper.DeleteJobByName(
                            webApp,
                            PictureLibraryResizerJob.GetJobToken(listId));
                    }

                    // Add the job.

                    if (configuration.Schedule != ImageResizerSchedule.Immediate)
                    {
                        PictureLibraryResizerJob resizerJob =
                            new PictureLibraryResizerJob(
                                webApp,
                                configuration);

                        Guid webId = this.webField.ID;
                        Guid siteId = this.webField.Site.ID;

                        resizerJob.SetPictureLibrary(siteId, webId, listId);

                        resizerJob.SubmitJob();
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Generates the picture library token.
        /// </summary>
        /// <param name="listId">The list id.</param>
        /// <returns>The picture library token for the configuration entry.</returns>
        protected string GeneratePictureLibraryToken(Guid listId)
        {
            string componentName = "WeMakeSP.ImageResizer";

            // Build the token.

            StringBuilder uniqueBagIdentifier = new StringBuilder();
            uniqueBagIdentifier.Append(componentName);
            uniqueBagIdentifier.Append(".");
            uniqueBagIdentifier.Append(listId);

            // Returns.

            return
                uniqueBagIdentifier.ToString();
        }

        #endregion
    }
}
